package com.dingwen.treasure.mybatisplus.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dingwen.treasure.base.enums.StatusEnum;
import com.dingwen.treasure.model.entity.mybatisplus.BusinessRule;
import com.dingwen.treasure.mybatisplus.service.BusinessRuleService;
import com.dingwen.treasure.mybatisplus.service.JavaScriptService;
import com.dingwen.treasure.mybatisplus.util.JavaScriptUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.script.CompiledScript;
import javax.script.ScriptException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 　JavaScriptServiceImpl
 * 　@author dingwen
 * 　@date 2022/8/29
 */
@Service
@Slf4j
public class JavaScriptServiceImpl implements JavaScriptService {


    /**
     * businessRuleService
     */
    @Resource
    private BusinessRuleService businessRuleService;


    /**
     * 添加结果
     *
     * @param fieldName  当前校验的字段名称
     * @param fieldValue 当前校验的值
     */
    @Override
    public void addResult(String fieldName, String fieldValue) {
        // 通过fileName查询规则信息
        // 从缓存中获取当前规则信息
        BusinessRule businessRule = new BusinessRule();

        log.info("fieldName:{}", fieldName);
        log.info("fieldValue:{}", fieldValue);

        // TODO 生成redis key 存储结果到redis （hash）
        // 所有执行完毕后从redis获取检查结果
    }


    /**
     * 运行 </br>
     * <p>示例脚本：function add(op1,op2){return op1+op2} add(a, b)</p>
     *  <p>可优化点：</p>
     *  <ul>
     *      <li>根据业务id异步添加校验结果以及进度到redis提供前端查询进度以及结果</li>
     *      <li>缓存、代码容错、性能</li>
     *  </ul>
     * @param businessId      业务id
     * @param needCheckValues 需要检查的值
     */
    @Override
    public void run(Map<String, Object> needCheckValues, Long businessId) {
        // 传递参数
        needCheckValues.forEach((k, v) -> {
            JavaScriptUtil.JAVA_SCRIPT_ENGINE.put(k, v);
        });

        List<Long> rules = getRules(businessId);

        rules.parallelStream()
                .forEach(ruleId -> {
                    CompiledScript compile = JavaScriptUtil.COMPILED_SCRIPT_MAP.get(ruleId);
                    Assert.notNull(compile, "预编译脚本获取异常");
                    try {
                        Object returnValue = compile.eval();
                        log.info("业务：{},规则：{}, JavaScript执行结果：{}", businessId, ruleId, returnValue);
                    } catch (ScriptException e) {
                        log.error("业务:{},规则:{},JavaScript规则校验执行错误:{}", businessId, ruleId, e.getMessage());
                    }
                });
    }

    /**
     * 获取指定业务需要进行的规则校验id集合
     *
     * @param businessId 业务标识
     * @return {@link List}<{@link Long}>
     */
    private List<Long> getRules(Long businessId) {
        // 查询指定业务所需要执行的规则校验id集合
        LambdaQueryWrapper<BusinessRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(BusinessRule::getRuleId)
                .eq(BusinessRule::getRuleState, StatusEnum.NORMAL)
                .eq(BusinessRule::getBusinessId, businessId);
        return businessRuleService.list(queryWrapper)
                .stream()
                .map(BusinessRule::getRuleId)
                .collect(Collectors.toList());
    }
}
